﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Close.io.Client;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Close.io.API
{
	public class CloseIo
	{
		public void CreateNote(string leadId, string text)
		{
			var payload = new JObject
			{
				{"lead_id", leadId},
				{"note", text}
			};

			var client = new CloseIoClient();
			CloseIoApiResult leadResult = Retry.Do(client.Post, CloseIoEndpoints.Note, leadId, payload.ToString(),
				TimeSpan.FromSeconds(1), 3);
			if (leadResult.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(leadResult.ReasonPhrase);
			}
		}


		public void CreateTask(string leadId, string assignedTo, string text, DateTime dueDate)
		{
			var payload = new JObject
			{
				{"lead_id", leadId},
				{"assigned_to", assignedTo},
				{"text", text},
				{
					"due_date", dueDate.ToUniversalTime()
						.ToString("s")
				},
				{"is_complete", false}
			};
			var client = new CloseIoClient();
			CloseIoApiResult leadResult = Retry.Do(client.Post, CloseIoEndpoints.Task, payload.ToString(),
				TimeSpan.FromSeconds(1), 3);
			if (leadResult.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(leadResult.ReasonPhrase);
			}
		}


		public void CreateLead(Lead lead)
		{
			AddLead(lead);
		}

		public void CreateOpportunity(Opportunity opportunity)
		{
			AddOpportunity(opportunity);
		}


		public Lead GetLead(string id)
		{
			var helper = new CloseIoHelper<Lead>();
			Lead lead = helper.Get(id);
			return lead;
		}


		public Lead GetLead(string id, IEnumerable<string> fields)
		{
			if (id == null) throw new ArgumentNullException("id");
			if (fields == null) throw new ArgumentNullException("fields");

			const string queryFormat = "?query={0}&_fields={1}";
			string query = string.Format(queryFormat, id, string.Join(", ", fields));
			var client = new CloseIoClient();
			CloseIoApiResult leadResult = Retry.Do(client.Get, CloseIoEndpoints.Lead, query, TimeSpan.FromSeconds(1));
			if (leadResult.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(leadResult.ReasonPhrase);
			}


			var leads = leadResult.ContentObject["data"].ToObject<JArray>();

			if (leads.Any())
				return leads.Children<JObject>()
					.FirstOrDefault()
					.ToObject<Lead>();

			return null;
		}

		public IEnumerable<Lead> GetLeads(CloseIoRequest request, out int count, out bool hasMore)
		{
			var client = new CloseIoClient();
			CloseIoApiResult leadResult = Retry.Do(client.Get, CloseIoEndpoints.Lead, string.Empty, request.ToString(),
				TimeSpan.FromSeconds(1));
			if (leadResult.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(leadResult.ReasonPhrase);
			}

			hasMore = leadResult.ContentObject["has_more"].ToString()
				.Equals("True");
			count = leadResult.ContentObject["total_results"].ToObject<int>();
			var leads = leadResult.ContentObject["data"].ToObject<IEnumerable<Lead>>();

			return leads;
		}

		public Lead AddLead(Lead lead)
		{
			var helper = new CloseIoHelper<Lead>();
			return helper.Add(lead);
		}

		public Lead UpdateLead(Lead lead)
		{
			var helper = new CloseIoHelper<Lead>();
			return helper.Update(lead);
		}

		public void UpdateLead(string leadId, JProperty property)
		{
			var client = new CloseIoClient();

			CloseIoApiResult leadResult = Retry.Do(client.Put, CloseIoEndpoints.Lead, leadId, property.ToString(),
				TimeSpan.FromSeconds(1));
			if (leadResult.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(leadResult.ReasonPhrase);
			}
		}

		public void UpdateLead(string leadId, IEnumerable<JProperty> properties)
		{
			var helper = new CloseIoHelper<Lead>();
			helper.Update(leadId, CloseIoEndpoints.Lead, properties);
		}

		public void UpdateLead<T>(string leadId, CloseIoUpdateBuilder<T> builder)
		{
			var helper = new CloseIoHelper<Lead>();
			helper.Update(leadId, builder.GetUpdateJson());
		}

		public void DeleteLead(string id)
		{
			var helper = new CloseIoHelper<Lead>();
			helper.Delete(id);
		}


		private JObject GetLeadCustomObject(string id)
		{
			const string queryFormat = "?query=id:{0}&_fields=id,custom";
			string query = string.Format(queryFormat, id);
			var client = new CloseIoClient();
			CloseIoApiResult leadResult = Retry.Do(client.Get, CloseIoEndpoints.Lead, query, TimeSpan.FromSeconds(1));
			if (leadResult.StatusCode != HttpStatusCode.OK)
			{
				throw new CloseIoException(leadResult.ReasonPhrase);
			}


			var leads = leadResult.ContentObject["data"].ToObject<JArray>();

			if (leads.Any())
				return leads.Children<JObject>()
					.FirstOrDefault();

			return null;
		}

		public void RemoveCustomField(string id, IEnumerable<string> fieldNames)
		{
			JObject custom = GetLeadCustomObject(id);
			RemoveFields(custom, fieldNames);
		}

		public void RemoveCustomField(string id, string fieldname)
		{
			RemoveCustomField(id, new[] {fieldname});
		}

		public void ProcessLeads(string id, int skip, int limit, Action<Lead> action, params string[] fields)
		{
			throw new NotImplementedException();
		}

		public void ProcessLeads(string id, int skip, int limit, Action<Lead> action, Func<Lead, bool> filter,
			params string[] fields)
		{
			throw new NotImplementedException();
		}


		public IEnumerable<Lead.Address> GetAddresses(string leadId)
		{
			Lead lead = GetLead(leadId, new[] {leadId, "addessses"});
			return lead.Addresses;
		}


		public IEnumerable<Contact> GetContacts(string leadId)
		{
			Lead lead = GetLead(leadId, new[] {leadId, "contacts"});
			return lead.Contacts;
		}

		public CustomFields GetCustomFields(string leadId)
		{
			Lead lead = GetLead(leadId, new[] {leadId, "custom"});
			return lead.Custom;
		}


		public Contact AddContact(Contact contact)
		{
			var helper = new CloseIoHelper<Contact>();
			return helper.Add(contact);
		}

		public void UpdateContact(string id, CloseIoUpdateBuilder<Contact> builder)
		{
			var helper = new CloseIoHelper<Contact>();
			helper.Update(id, builder.GetUpdateJson());
		}

		public void DeleteContact(string id)
		{
			var helper = new CloseIoHelper<Contact>();
			helper.Delete(id);
		}


		public IEnumerable<Activity> GetActivities(ActivityQueryBuilder parameters,
			CloseIoFieldListBuilder<Activity> builder = null)
		{
			if (builder != null)
			{
				string fields = builder.ToString();
				parameters.Add("_fields", fields);
			}
			var helper = new CloseIoHelper<Activity>();
			return helper.Get(parameters.ToParameterDictionary());
		}

		public IEnumerable<Note> GetNotes(ActivityQueryBuilder parameters)
		{
			var helper = new CloseIoHelper<Note>();
			return helper.Get(parameters.ToParameterDictionary());
		}

		public IEnumerable<Email> GetEmails(ActivityQueryBuilder parameters)
		{
			var helper = new CloseIoHelper<Email>();
			return helper.Get(parameters.ToParameterDictionary());
		}

		public IEnumerable<EmailThread> GetEmailThreads(ActivityQueryBuilder parameters)
		{
			var helper = new CloseIoHelper<EmailThread>();
			return helper.Get(parameters.ToParameterDictionary());
		}

		public IEnumerable<Call> GetCalls(ActivityQueryBuilder parameters)
		{
			var helper = new CloseIoHelper<Call>();
			return helper.Get(parameters.ToParameterDictionary());
		}


		public IEnumerable<StatusChange> GetStatusChanges(string leadId, string userId, DateTime? GreaterThanDate,
			DateTime? LessThanDate)
		{
			return null;
		}

		public IEnumerable<OpportunityStatusChange> GetOpportunityChanges(string leadId, string userId,
			DateTime? GreaterThanDate, DateTime? LessThanDate)
		{
			return null;
		}

		public void SendEmail(Email email)
		{
		}

		public void CreateDraftEmail(Email email)
		{
		}

		public void UpdateDraftEmail(Email email)
		{
		}

		public void LogCall(CallLog callLog)
		{
		}

		public void DeleteEmail(string id)
		{
			var helper = new CloseIoHelper<Email>();
			helper.Delete(id);
		}

		public void DeleteCall(string id)
		{
			var helper = new CloseIoHelper<Call>();
			helper.Delete(id);
		}

		public void DeleteNote(string id)
		{
			var helper = new CloseIoHelper<Note>();
			helper.Delete(id);
		}

		public void DeleteStatusChange(string id)
		{
			var helper = new CloseIoHelper<StatusChange>();
			helper.Delete(id);
		}

		public void DeleteOpportunityStatusChange(string id)
		{
			var helper = new CloseIoHelper<OpportunityStatusChange>();
			helper.Delete(id);
		}

		public Opportunity GetOpportunity(string id)
		{
			var helper = new CloseIoHelper<Opportunity>();
			return helper.Get(id);
		}

		public IEnumerable<Opportunity> GetOpportunities(string query)
		{
			return null;
		}

		public void AddOpportunity(Opportunity opportunity)
		{
			string payload = JsonConvert.SerializeObject(opportunity);
			var helper = new CloseIoHelper<Opportunity>();
			helper.Add(payload);
		}

		public void UpdateOpportunity(string id, CloseIoUpdateBuilder<Opportunity> builder)
		{
			var helper = new CloseIoHelper<Opportunity>();
			helper.Update(id, builder.GetUpdateJson());
		}

		public void DeleteOpportunity(string id)
		{
			var helper = new CloseIoHelper<Opportunity>();
			helper.Delete(id);
		}

		public Task GetTask(string id)
		{
			var helper = new CloseIoHelper<Task>();
			return helper.Get(id);
		}

		public IEnumerable<Task> GetTasks(TaskQueryBuilder parameters)
		{
			var helper = new CloseIoHelper<Task>();
			return helper.Get(parameters.ToParameterDictionary());
		}

		public void UpdateTask(string id, CloseIoUpdateBuilder<Task> builder)
		{
			var helper = new CloseIoHelper<Opportunity>();
			helper.Update(id, builder.GetUpdateJson());
		}

		public void DeleteTask(string id)
		{
			var helper = new CloseIoHelper<Task>();
			helper.Delete(id);
		}

		//public void Backup(bool includeActivities
		//{
		//	const string payloadFormat = "{ \"format\":\"json\", \"type\": \"leads\",\"include_activities\": {0}}}";
		//	const string endPoint = "export/lead";
		//	string id = "lead";


		//	var client = new CloseIoClient();
		//	client.Post(endPoint, id, payload);
		//}

		private void RemoveFields(JObject token, IEnumerable<string> fields)
		{
			JContainer container = token;
			if (container == null) return;

			var removeList = new List<JToken>();
			foreach (JToken el in container.Children())
			{
				var p = el as JProperty;
				if (p != null && fields.Contains(p.Name))
				{
					removeList.Add(el);
				}
			}

			foreach (JToken el in removeList)
			{
				el.Remove();
			}
		}
	}
}